﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ITAP.ShprossesBuilder.Modelling.Components;
using System.Drawing;
using ITAP.Core.Modelling.Primitives;
using System.Drawing.Drawing2D;
using ITAP.ShprossesBuilder.Editor;
using ITAP.ShprossesBuilder.Modelling;
using ITAP.Core.Modelling;

namespace ITAP.ShprossesBuilder.Render
{
    public class Render : SimpleRender
    {

        public Render()
            : base()
        {
            
        }


        public override void Visit(SpacerConnector spacerConnector)
        {
            Color color = spacerConnector.Color;
            Color borderColor = spacerConnector.BorderColor;

            if (spacerConnector.SpacerConnectorType == SpacerConnectorType.Internal)
            {
                color = Color.FromArgb(100, spacerConnector.Color.R, spacerConnector.Color.G, spacerConnector.Color.B);
                borderColor = Color.FromArgb(100, spacerConnector.BorderColor.R, spacerConnector.BorderColor.G, spacerConnector.BorderColor.B);
            }
            LinePrimitive spacerConnectorPrimitive = (LinePrimitive)spacerConnector.Primitives[0];
            this.DrawLinePrimitive(spacerConnectorPrimitive, color, borderColor, 0, 1, true, true);
        }

        public override void Visit(ShprossConnector shprossConnector)
        {
            base.Visit(shprossConnector);
        }

        public override void Visit(ShprossConnectorStub stub)
        {
            GraphicsPath path = new GraphicsPath();
            

            PointF point1;
            PointF point2;
            path.StartFigure();
            foreach (IPrimitive primitive in stub.Primitives[0].InnerPrimitives)
            {
                point1 = coordinateConverter.ModelPointToScreenPoint(primitive.GetPoint(0), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                point2 = coordinateConverter.ModelPointToScreenPoint(primitive.GetPoint(1), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                path.AddLine(point1, point2);
            }
            path.CloseFigure();
            if (stub.Parent.Selected)
            {

                ModelContainer container = stub.GetRootContainer(); // stub.Parent.Parent.Parent.Parent as ModelContainer;

                if(container.SelectedComponentsStack.IndexOf(stub.Parent) == 0)
                    this.SetBrush(this.firstSelectedComponentColor);
                else
                    this.SetBrush(this.selectedComponentColor);
            }
            else
                this.SetBrush(stub.Color);
            graphics.FillPath(brush, path);
            this.SetPen(stub.BorderColor, 1);
            graphics.DrawPath(pen, path);
        }

        public override void Visit(SnapOnCap snapOnCap)
        {
            List<PointF> points = new List<PointF>();
            List<byte> bytes = new List<byte>();
            GraphicsPath path = new GraphicsPath();
            path.StartFigure();
            foreach (IEndedPrimitive line in snapOnCap.Primitives[0].InnerPrimitives)
            {
                PointF point1 = coordinateConverter.ModelPointToScreenPoint(line.StartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
                PointF point2 = coordinateConverter.ModelPointToScreenPoint(line.EndPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
                path.AddLine(point1, point2);
            }

            double opacity = (double)snapOnCap.Opacity / 100;
            if (snapOnCap.Selected)
            {
                this.SetBrush(Color.FromArgb((int)(255 * opacity), selectedComponentColor));
                graphics.FillPath(brush, path);
                this.SetPen(Color.FromArgb((int)(255 * opacity), snapOnCap.BorderColor), 1);
                graphics.DrawPath(pen, path);
            }
            else
            {
                this.SetBrush(Color.FromArgb((int)(255 * opacity), snapOnCap.Color));
                graphics.FillPath(brush, path);
                this.SetPen(Color.FromArgb((int)(255 * opacity), snapOnCap.BorderColor), 1);
                graphics.DrawPath(pen, path);

            }
            RectangleF bbox = snapOnCap.BoundingBox;
            if (!snapOnCap.IsAngleIntervalsCorrect())
            {
                this.DrawErrorCircle(snapOnCap.Position, Math.Max(bbox.Width, bbox.Height) / 2, 3, 5);
                //RectangleF rect = coordinateConverter.ModelRectangleToScreenRectangle(snapOnCap.BoundingBox, screenWidth, screenHeight, modelRectangle, this.centerPoint);
                //this.SetBrush(Color.FromArgb(30, 255, 0, 0));
                //rect = new RectangleF(rect.X - 9, rect.Y - 9, rect.Width + 18, rect.Height + 18);
                //graphics.FillEllipse(brush, rect);
            }

        }

        public override void Visit(Connector connector)
        {            
            LinePrimitive connectorPrimitive = connector.Primitives[0] as LinePrimitive;
            if (connector.Parent.Selected)
            {
                ModelContainer container = connector.GetRootContainer(); // connector.Parent.Parent.Parent.Parent as ModelContainer;
                if(container.SelectedComponentsStack.IndexOf(connector.Parent) == 0)
                    this.DrawLinePrimitive(connectorPrimitive, this.firstSelectedComponentColor, connector.BorderColor, 0, 1, true, true);
                else
                    this.DrawLinePrimitive(connectorPrimitive, this.selectedComponentColor, connector.BorderColor, 0, 1, true, true);
            }
            else
                this.DrawLinePrimitive(connectorPrimitive, connector.Color, connector.BorderColor, 0, 1, true, true);


        }

        protected virtual void DrawShpross(Shpross shpross, bool drawStartBorder, bool drawEndBorder)
        {

            if (shpross.Height != 0)
            {
                CirclePrimitive shprossCirclePrimitive = shpross.Primitives[0] as CirclePrimitive;
                this.DrawCirclePrimitive(shprossCirclePrimitive, shpross.Color, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);


                if (shpross.Selected)
                {
                    if (shpross.Parent.Parent.Parent is ModelContainer)
                    {
                        ModelContainer container = shpross.Parent.Parent.Parent as ModelContainer;
                        if (container.SelectedComponentsStack.IndexOf(shpross) == 0)
                        {
                            this.DrawCirclePrimitive(shprossCirclePrimitive, this.firstSelectedComponentColor, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);
                        }
                        else
                            this.DrawCirclePrimitive(shprossCirclePrimitive, this.selectedComponentColor, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);
                    }
                    else
                        throw new Exception();
                    
                    if (GlobalPreferences.Preferences.ShowShprossSnaps)
                    {
                        this.DrawShprossHook(shpross.StartHook, Color.FromArgb(60, Color.Green));
                        this.DrawShprossHook(shpross.CenterHook, Color.FromArgb(60, Color.Red));
                        this.DrawShprossHook(shpross.EndHook, Color.FromArgb(60, Color.Blue));
                    }

                    switch (shpross.SelectedPoint)
                    {
                        case ShprossPoints.Start:
                            this.DrawShprossHook(shpross.StartHook, Color.Green);
                            break;
                        case ShprossPoints.Center:
                            this.DrawShprossHook(shpross.CenterHook, Color.Red);
                            break;
                        case ShprossPoints.End:
                            this.DrawShprossHook(shpross.EndHook, Color.Blue);
                            break;
                    }
                }
                else
                {
                    if (GlobalPreferences.Preferences.ShowShprossSnaps)
                    {
                        this.DrawShprossHook(shpross.StartHook, Color.FromArgb(60, Color.Green));
                        this.DrawShprossHook(shpross.CenterHook, Color.FromArgb(60, Color.Red));
                        this.DrawShprossHook(shpross.EndHook, Color.FromArgb(60, Color.Blue));
                    }
                }
            }
            else
            {
                LinePrimitive shprossLinePrimitive = shpross.Primitives[0] as LinePrimitive;
                this.DrawLinePrimitive(shprossLinePrimitive, shpross.Color, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);

                if (shpross.Selected)
                {

                    if (shpross.Parent.Parent.Parent is ModelContainer)
                    {
                        ModelContainer container = shpross.Parent.Parent.Parent as ModelContainer;
                        if (container.SelectedComponentsStack.IndexOf(shpross) == 0)
                            this.DrawLinePrimitive(shprossLinePrimitive, this.firstSelectedComponentColor, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);
                        else
                            this.DrawLinePrimitive(shprossLinePrimitive, this.selectedComponentColor, shpross.BorderColor, 0, 1, drawStartBorder, drawEndBorder);
                    }
                    else
                        throw new Exception();

                    if (GlobalPreferences.Preferences.ShowShprossSnaps)
                    {
                        this.DrawShprossHook(shpross.StartHook, Color.FromArgb(60, Color.Green));
                        this.DrawShprossHook(shpross.CenterHook, Color.FromArgb(60, Color.Red));
                        this.DrawShprossHook(shpross.EndHook, Color.FromArgb(60, Color.Blue));
                    }
                    switch (shpross.SelectedPoint)
                    {
                        case ShprossPoints.Start:
                            this.DrawShprossHook(shpross.StartHook, Color.Green);
                            break;
                        case ShprossPoints.Center:
                            this.DrawShprossHook(shpross.CenterHook, Color.Red);
                            break;
                        case ShprossPoints.End:
                            this.DrawShprossHook(shpross.EndHook, Color.Blue);
                            break;
                    }
                }
                else
                {
                    if (GlobalPreferences.Preferences.ShowShprossSnaps)
                    {
                        this.DrawShprossHook(shpross.StartHook, Color.FromArgb(60, Color.Green));
                        this.DrawShprossHook(shpross.CenterHook, Color.FromArgb(60, Color.Red));
                        this.DrawShprossHook(shpross.EndHook, Color.FromArgb(60, Color.Blue));
                    }
                }
            }

            if (shpross.StartConnectedComponent == null)
            {
                this.DrawErrorCircle(shpross.StartPoint, (float)shpross.Width, 3, 5);
            }
            if (shpross.EndConnectedComponent == null)
            {
                this.DrawErrorCircle(shpross.EndPoint, (float)shpross.Width, 3, 5);
            }

        }

        public override void Visit(Shpross shpross)
        {
            if (shpross.Chain != null)
            {
                return;
            }

            this.DrawShpross(shpross, true, true);
        }

        public virtual void DrawShprossHook(ShprossHook shprossHook, Color color)
        {
            PointF screenPosition = coordinateConverter.ModelPointToScreenPoint(shprossHook.Position, 
                screenWidth, screenHeight, this.modelRectangle, this.centerPoint);
            float radius = coordinateConverter.ModelMeasureToScreenMeasure((float)shprossHook.Radius, screenWidth, screenHeight, 
                this.modelRectangle);

            radius = radius * 0.9F;

            this.SetBrush(color);
            RectangleF rect = new RectangleF(screenPosition.X - radius, screenPosition.Y - radius, 2 * radius, 2 * radius);
          //  rect = new Rectangle(rect.X + 2, rect.Y + 2, rect.Width - 4, rect.Height - 4);
            try
            {
                graphics.FillEllipse(brush, rect);
                int r = color.R - 70;
                int g = color.G - 70;
                int b = color.B - 70;
                if (r < 0) r = 0;
                if (g < 0) g = 0;
                if (b < 0) b = 0;
                this.SetPen(Color.FromArgb(255, r, g, b), 1);
                graphics.DrawEllipse(pen, rect);
            }
            catch
            {

            }
        }

        protected virtual void DrawLinePrimitive(LinePrimitive linePrimitive, Color color, Color borderColor, double startOffset, double endOffset, bool drawStartBorder, bool drawEndBorder)
        {
            PointF point1;
            PointF point2;
            PointF point3;
            PointF point4;
            GraphicsPath path = new GraphicsPath();

            path.StartFigure();
            point1 = coordinateConverter.ModelPointToScreenPoint(linePrimitive.DownBorder.GetPoint(startOffset), screenWidth, ScreenHeight, modelRectangle, this.centerPoint);
            point2 = coordinateConverter.ModelPointToScreenPoint(linePrimitive.UpBorder.GetPoint(startOffset), screenWidth, ScreenHeight, modelRectangle, this.centerPoint);
            path.AddLine(point1, point2);

            point3 = coordinateConverter.ModelPointToScreenPoint(linePrimitive.UpBorder.GetPoint(endOffset), screenWidth, ScreenHeight, modelRectangle, this.centerPoint);
            path.AddLine(point2, point3);

            point4 = coordinateConverter.ModelPointToScreenPoint(linePrimitive.DownBorder.GetPoint(endOffset), screenWidth, ScreenHeight, modelRectangle, this.centerPoint);
            path.AddLine(point3, point4);

            path.AddLine(point4, point1);
            path.CloseFigure();
            this.SetBrush(color);
            graphics.FillPath(brush, path);

            this.SetPen(borderColor, 1);
            if (startOffset == 0 && drawStartBorder)
                graphics.DrawLine(pen, point1, point2);
            if (endOffset == 1 && drawEndBorder)
                graphics.DrawLine(pen, point3, point4);

            graphics.DrawLine(pen, point2, point3);
            graphics.DrawLine(pen, point4, point1);
            path.Dispose();
        }

        public override void Visit(SpacerElement spacerElement)
        {
            if (spacerElement.Height != 0)
                this.DrawCirclePrimitive(spacerElement.Primitives[0] as CirclePrimitive, Color.DarkGray, Color.Black, 0, 1, true, true);
            else
                this.DrawLinePrimitive(spacerElement.Primitives[0] as LinePrimitive, Color.DarkGray, Color.Black, 0, 1, true, true);
        }

        protected virtual void DrawCirclePrimitiveUsingGDI(CirclePrimitive circlePrimitive, Color color, Color borderColor, double startOffset, double endOffset, bool drawStartBorder, bool drawEndBorder)
        {
            RectangleF rectangle;
            float startAngle;
            float sweepAngle;

            this.SetBrush(color);
            this.SetPen(borderColor, 1);

            GraphicsPath path = new GraphicsPath();
            path.StartFigure();

            ModelPoint upStartPoint = circlePrimitive.UpBorder.GetPoint(startOffset);
            ModelPoint downStartPoint = circlePrimitive.DownBorder.GetPoint(startOffset);

            PointF point1 = coordinateConverter.ModelPointToScreenPoint(upStartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
            PointF point2 = coordinateConverter.ModelPointToScreenPoint(downStartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
            path.AddLine(point1, point2);
            this.GetArcParameters(circlePrimitive.DownBorder as Circle, startOffset, endOffset, out rectangle, out startAngle, out sweepAngle);
            path.AddArc(rectangle, startAngle, sweepAngle);
            point1 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.DownBorder.GetPoint(endOffset), screenWidth, screenHeight, modelRectangle, this.centerPoint);
            point2 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.UpBorder.GetPoint(endOffset), screenWidth, screenHeight, modelRectangle, this.centerPoint);
            path.AddLine(point1, point2);
            this.GetArcParameters(circlePrimitive.UpBorder as Circle, endOffset, startOffset, out rectangle, out startAngle, out sweepAngle);
            path.AddArc(rectangle, startAngle, sweepAngle);
            path.CloseFigure();

            graphics.FillPath(brush, path);

            if (drawStartBorder && drawEndBorder)
            {
                graphics.DrawPath(pen, path);
            }
            else
            {
                this.GetArcParameters(circlePrimitive.UpBorder as Circle, endOffset, startOffset, out rectangle, out startAngle, out sweepAngle);
                graphics.DrawArc(pen, rectangle, startAngle, sweepAngle);
                this.GetArcParameters(circlePrimitive.DownBorder as Circle, endOffset, startOffset, out rectangle, out startAngle, out sweepAngle);
                graphics.DrawArc(pen, rectangle, startAngle, sweepAngle);
                if (drawStartBorder)
                {
                    point1 = coordinateConverter.ModelPointToScreenPoint(upStartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
                    point2 = coordinateConverter.ModelPointToScreenPoint(downStartPoint, screenWidth, screenHeight, modelRectangle, this.centerPoint);
                    graphics.DrawLine(pen, point1, point2);
                }
                if (drawEndBorder)
                {
                    point1 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.DownBorder.GetPoint(endOffset), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                    point2 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.UpBorder.GetPoint(endOffset), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                    graphics.DrawLine(pen, point1, point2);
                }
            }
        }

        protected virtual void DrawCirclePrimitive(CirclePrimitive circlePrimitive, Color color, Color borderColor, double startOffset, double endOffset, bool drawStartBorder, bool drawEndBorder)
        {

            this.DrawCirclePrimitiveUsingGDI(circlePrimitive, color, borderColor, startOffset, endOffset, drawStartBorder, drawEndBorder);
            return;


            int curveStepsCount = this.GetCurveStepsCount(coordinateConverter.Scale, circlePrimitive.Length);
            double curveStep = 1 / (double)curveStepsCount;

            PointF startPoint2;
            PointF endPoint2;
            PointF startPoint3;
            PointF endPoint3;
            this.SetBrush(color);
            this.SetPen(borderColor, 1);
            for (double i = startOffset; i <= endOffset + curveStep; i += curveStep)
            {
                // Нижняя граница
                startPoint2 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.DownBorder.GetPoint(i), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                endPoint2 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.DownBorder.GetPoint(i + curveStep), screenWidth, screenHeight, modelRectangle, this.centerPoint);

                // Верхняя граница
                startPoint3 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.UpBorder.GetPoint(i), screenWidth, screenHeight, modelRectangle, this.centerPoint);
                endPoint3 = coordinateConverter.ModelPointToScreenPoint(circlePrimitive.UpBorder.GetPoint(i + curveStep), screenWidth, screenHeight, modelRectangle, this.centerPoint);

                if (screenRectangle.Contains(startPoint3) || screenRectangle.Contains(endPoint3) || screenRectangle.Contains(startPoint2) || screenRectangle.Contains(endPoint2))
                    graphics.FillPath(brush, new GraphicsPath(new PointF[] { startPoint2, endPoint2, endPoint3, startPoint3 }, new byte[] { (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line }));

                if (screenRectangle.Contains(startPoint2) || screenRectangle.Contains(endPoint2))
                    graphics.DrawLine(pen, startPoint2, endPoint2);

                if (screenRectangle.Contains(startPoint3) || screenRectangle.Contains(endPoint3))
                    graphics.DrawLine(pen, startPoint3, endPoint3);

                if (i == 0)
                    graphics.DrawLine(pen, startPoint2, startPoint3);
                if (i >= 1)
                    graphics.DrawLine(pen, endPoint2, endPoint3);
            }

        }

        public override void Visit(ShprossChain shprossChain)
        {
            for (int i = 0; i < shprossChain.ConnectedComponents.Count; i++)
            {
                Shpross shpross = shprossChain.ConnectedComponents[i] as Shpross;
                if (i == 0 || i == shprossChain.ConnectedComponents.Count - 1)
                {
                    if (shpross.StartConnectedComponent is ShprossEndToEndConnector)
                    {
                        this.DrawShpross(shpross, false, true);
                    }
                    else
                    {
                        this.DrawShpross(shpross, true, false);
                    }
                }
                else
                {
                    this.DrawShpross(shpross, false, false);
                }
            }
        }
    }
}
